package dataDraw.stateDiagramDraw;

import data.Position;
import data.stateDiagram.ConditionalTransition;
import data.stateDiagram.State;
import data.stateDiagram.Transition;
import dataDraw.Constants;
import dataDraw.TextDraw;
import tools.*;

import java.awt.*;
import java.awt.Point;

/**
 * User: Szymon Kudzia
 * Date: 11.05.13
 * Time: 14:02
 */
public class BasicStateConnectionDraw implements StateConnectionDraw {
	protected Graphics g;
	protected TextDraw textDraw;

	@Override
	public void draw(Transition transition, Graphics graphics) {
		g = graphics;
		textDraw.setGraphics(graphics);

		if (transition instanceof ConditionalTransition)
			drawConditionalConnection((ConditionalTransition)transition);
		else
			drawStateConnection(transition.getSource(), transition.getDestination(), transition.toString());
	}


	private void drawConditionalConnection(ConditionalTransition transition)
	{
        String str = transition.getTrigger().getName() + "[" +
                transition.getCondition() + " == " + transition.getTrueExpression()
                + "]/" + transition.getAction() + "^" + transition.getMsgs();

        String fstr = transition.getTrigger().getName() + "[" +
                transition.getCondition() + " == " + transition.getFalseExpression()
                + "]/" + transition.getFailureAction() + "^" + transition.getFailureMsg();

        Position src = transition.getSource().getPosition();
        Position dst = transition.getDestination().getPosition();
        Position fdst = transition.getFailureDestination().getPosition();

        drawCondtionalConnection(src.getMiddleUp(), dst.getMiddleUp(), str, 30);
        drawCondtionalConnection(src.getMiddleUp(), fdst.getMiddleUp(), fstr, 30 + getStringSize(fstr).y);
	}

	private void drawStateConnection(State source, State destination, String transition)
	{
		drawStateConnection(source, destination, transition, 30);
	}

	private void drawStateConnection(State source, State destination, String transition, int h) {
		Position src = source.getPosition();
		Position dst = destination.getPosition();

		if (src == dst) {
			drawSelfConnection(src.getLeftMiddle(), dst.getMiddleUp(), transition);
		} else if (src.getFirstX() <= dst.getFirstX()) {
			drawConnection(src.getRightDown(), dst.getLeftDown(), transition, h);
		} else {
			drawConnection(src.getLeftUp(), dst.getRightUp(), transition, h);
		}
	}

	private void drawSelfConnection(tools.Point p1, tools.Point p2, String transition) {
        g.drawLine(p1.x, p1.y, p1.x - 30, p1.y);
        g.drawLine(p1.x - 30, p1.y, p1.x - 30, p2.y - 30);
        g.drawLine(p1.x - 30, p2.y - 30, p2.x, p2.y - 30);
        g.drawLine(p2.x, p2.y - 30, p2.x, p2.y);

        g.drawLine(p2.x, p2.y, p2.x - Constants.TRIANGLE_HALF_A,
                   p2.y - Constants.TRIANGLE_H);

        g.drawLine(p2.x, p2.y, p2.x + Constants.TRIANGLE_HALF_A,
                   p2.y - Constants.TRIANGLE_H);

        Point strSize = getStringSize(transition);
        Point strPoint = new Point(p1.x-30, p2.y);
        strPoint.x += Math.abs(strPoint.x - p2.x)/2 - strSize.x/2;
        strPoint.y -= 30 + strSize.y;

		textDraw.drawString(transition, new Position(strPoint.x,
				strPoint.y - Constants.MARGIN));
	}

	private void drawConnection(tools.Point p1, tools.Point p2, String transition)
	{
		drawConnection(p1, p2, transition, 30);
	}

	private void drawConnection(tools.Point p1, tools.Point p2, String transition, int h) {
        int s = 1;
        Point strPoint = new Point(p1.x, p1.y);
        Point strSize = getStringSize(transition);

        if (p1.x > p2.x) {
            s = -1;
            strPoint = new Point(p2.x, p2.y);
        }
        strPoint.x += Math.abs(p1.x - p2.x)/2 - strSize.x/2;

        int difference = p1.y - p2.y;
        int H = Math.max(h, h - difference);
        int H2 = Math.max(h, h + difference);

        if (s == -1) {
            strPoint.y -= strSize.y;
            H = Math.max(h, h + difference);
            H2 = Math.max(h, h - difference);
            strPoint.y += s * (-H + H2);
        }
        strPoint.y += s * H;

        g.drawLine(p1.x, p1.y, p1.x, p1.y + s * H);
        g.drawLine(p1.x, p1.y + s * H, p2.x, p2.y + s * H2);
        g.drawLine(p2.x, p2.y + s * H2, p2.x, p2.y);

        g.drawLine(p2.x, p2.y, p2.x - Constants.TRIANGLE_HALF_A,
                   p2.y + s * (Constants.TRIANGLE_H));

        g.drawLine(p2.x, p2.y, p2.x + Constants.TRIANGLE_HALF_A,
                   p2.y + s * (Constants.TRIANGLE_H));

		textDraw.drawString(transition, new Position(strPoint.x,
				strPoint.y - Constants.MARGIN));
	}

	private void drawCondtionalConnection(tools.Point p1, tools.Point p2, String transition, int h) {
		int s = -1;
        int s2 = 1;
        Point strPoint = new Point(p1.x, p1.y);
        Point strSize = getStringSize(transition);

        if (p1.x > p2.x) {
            s2 = -1;
            strPoint = new Point(p2.x, p2.y);
        }
        strPoint.x += Math.abs(p1.x - p2.x)/2 - strSize.x/2;

        int difference = p1.y - p2.y;
        int H = Math.max(h, h + difference);
        int H2 = Math.max(h, h - difference);

        if (s2 == 1) {
            strPoint.y += s * Math.max(h, h + difference) - strSize.y;
        }
        else
        {
            strPoint.y -= strSize.y;
            strPoint.y += s * H2;
        }


        g.drawLine(p1.x, p1.y, p1.x, p1.y + s * H);
        g.drawLine(p1.x, p1.y + s * H, p2.x, p2.y + s * H2);
        g.drawLine(p2.x, p2.y + s * H2, p2.x, p2.y);

        g.drawLine(p2.x, p2.y, p2.x - Constants.TRIANGLE_HALF_A,
                   p2.y + s * (Constants.TRIANGLE_H));

        g.drawLine(p2.x, p2.y, p2.x + Constants.TRIANGLE_HALF_A,
                   p2.y + s * (Constants.TRIANGLE_H));

        textDraw.drawString(transition, new Position(strPoint.x,
                                         strPoint.y - Constants.MARGIN));
	}

	protected Point getStringSize(String str) {
		return new Point(g.getFontMetrics().stringWidth(str), g.getFontMetrics().getHeight());
	}

	public void setTextDraw(TextDraw textDraw) {
		this.textDraw = textDraw;
	}
}
